classdef BatHandle < handle
    
    properties ( SetAccess = protected )
        name
    end
    
    properties ( Hidden = true )
        userData
        superData
        childData
    end
    
    methods % Set methods
        function [  ] = setName( this, name )
            if nargin == 1
                name = '';
            end
            
            assert(ischar(name), ...
                'BatErr: $name$ MUST be char.');
            
            this.name = name;
        end
    end
    
    methods % Construct method
        function [ this ] = BatHandle(  )
            if nargin == 0
                this.setName('');
            end
        end
    end
    
    methods
        function [ that ] = clone( this )
            %CLONE Clone `@BatHandle` object
            %
            % BatHandle::clone( this:BatHandle )
            %          >>     [ that:BatHandle ]
            %
            % Description
            % Generate `that` as a clone of `this` object
            %
            % Input
            % 1. this : BatHandle object which is to be cloned
            %
            % Output
            % 1. that : BatHandle object which is to be a clone of `this`
            %
            % Syntax
            % ~. that = this.clone();
            %
            % Example
            % ~. anotherBatHandle = batHandle.clone();
            
            %% New $that$
            
            that = eval(class(this));
            
            %% Generate meta class object
            
            mc = eval(['?', class(this)]);
            
            %% Find and assign 'public' properties
            
            obj = findobj([mc.Properties{:}], ...
                'Dependent', false, ...
                'SetAccess', 'public');
            
            for itr = 1:length(obj)
                propName = obj(itr).Name;
                assert(ischar(propName)); % Optional
                
                % Not use class set methods
                if ~isa(this.(propName), 'BatHandle')
                    that.(propName) = this.(propName);
                else%if is a `@BatHandle` object
                    that.(propName) = this.(propName).clone();
                end
            end
            
            %% Find and assign 'protected' and 'private' properities
            
            obj = [findobj([mc.Properties{:}], ...
                'Dependent', false, ...
                'SetAccess', 'protected'); ...
                findobj([mc.Properties{:}], ...
                'Dependent', false, ...
                'SetAccess', 'private')];
            
            for itr = 1:length(obj)
                propName = obj(itr).Name;
                setMethodName = ['set', ...
                    upper(propName(1)), propName(2:end)];
                
                % Use class set methods
                if ~isa(this.(propName), 'BatHandle')
                    eval(['that.', setMethodName, '(this.', propName, ');']);
                else%if is a `@BatHandle` object
                    eval(['that.', setMethodName, '(this.', propName, '.clone());']);
                end
            end
        end
        
        function [  ] = duplicate( this, that )
            %DUPLICATE Duplicate `@BatHandle` object
            %
            % BatHandle::duplicate( this:BatHandle,
            %                       that:BatHandle )
            %          >>         [  ]
            %
            %Description
            % Let `this` duplicate `that`
            %
            %Input
            % 1. this : `@BatHandle` object which is to be same as `that`
            % 2. that : `@BatHandle` object to be duplicated
            %
            %Output
            % 0.
            %
            %Syntax
            % ~. this.duplicate(that);
            %
            %Example
            % ~. batHandle.duplicate(anotherBatHandle);
            
            
            %% Check input
            
            assert(isequal(class(this), class(that)), ...
                'BatErr: Class of `this` MUST equal class of `that`.');
            
            %% Generate meta class object
            
            mc = eval(['?', class(this)]);
            
            %% Find and assign 'public' and 'protected' properties
            
            obj = findobj([mc.Properties{:}], ...
                'Dependent', false, ...
                'SetAccess', 'public');
            
            for itr = 1:length(obj)
                propName = obj(itr).Name;
                assert(ischar(propName)); % Optional
                
                % Not use class set methods
                if ~isa(that.(propName), 'BatHandle')
                    this.(propName) = that.(propName);
                else%if is a `@BatHandle` object
                    this.(propName).delete();
                    this.(propName) = that.(propName).clone();
                end
            end
            
            %% Find and assign 'private' properities
            
            obj = [findobj([mc.Properties{:}], ...
                'Dependent', false, ...
                'SetAccess', 'protected'); ...
                findobj([mc.Properties{:}], ...
                'Dependent', false, ...
                'SetAccess', 'private')];
            
            for itr = 1:length(obj)
                propName = obj(itr).Name;
                setMethodName = ['set', ...
                    upper(propName(1)), propName(2:end)];
                
                % Use class set methods
                if ~isa(that.(propName), 'BatHandle')
                    eval(['this.', setMethodName, '(that.', propName, ');']);
                else%if is a `@BatHandle` object
                    this.(propName).delete();
                    eval(['this.', setMethodName, '(that.', propName, '.clone());']);
                end
            end
        end
        
        function [  ] = help( this, dispType )
            % BatHandle::help( this:BatHandle,
            %                  dispType:{char, double}
            %          >>    [  ]
            %
            % Description
            % Display the help text of the @BatHandle object
            %
            % Input
            % 1. this : @BatHandle object
            % 2. dispType : {'cmd, 'c', 'command', 1} for command line display
            %               or {'win', 'w', 'window', 2} for window display
            %
            % Output
            % 0.
            %
            % Syntax
            % ~. this.help(); % Eq. this.help('cmd');
            % ~. this.help(dispType);
            %
            % Example
            % ~. batHandle.help();
            % ~. batHandle.help('cmd');
            % ~. batHandle.help(1);
            % ~. batHandle.help('win');
            % ~. batHandle.help(2);
            
            if nargin == 1
                dispType = 'cmd';
            end
            
            className = class(this);
            
            switch lower(dispType)
                case {'cmd', 'c', 'command', 1}
                    help(className);
                case {'win', 'w', 'window', 2}
                    helpwin(className)
                otherwise
                    error('BatErr: Invalid $dispTpye$.');
            end
        end
    end
    
end
